#include <iostream>
#include <string>
#include <cstring>
#include <sys/select.h>
#include "Sock.hpp"
#include "Log.hpp"

const static int gport = 8888;

typedef int type_t;
// const int defaultfd = -1;

class SelectServer
{
    static const int N = (sizeof(fd_set) * 8);

public:
    SelectServer(uint16_t port = gport)
        : _port(port)
    {
    }
    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(_port);
        listensock_.Listen();
        //初始化数组
        for(int i = 0; i < N; ++i) fdarray_[i] = defaultfd;
    }

    void Accepter()
    {
        // std::cout << "有一个链接到来了" << std::endl;
        // 说明有一个新链接到来了，这时再去进行accept就不会杯阻塞，因为有数据
        std::string clientip;
        uint16_t clientport;
        int sock = listensock_.Accept(&clientip, &clientport);
        // 但是我们还不能进行读取操作read/recv,因为我们不能保证这次链接是否有数据进行传递
        // 所有我们需要将新的fd也交给select管理，当该链接有数据传送来的时候，我们再进行读取操作
        int i = 0;
        for(; i < N; ++i)
        {
            //找到没被占用的位置
            if(fdarray_[i] == defaultfd) break;
        }
        if(i >= N)
        {
            //没找到，说明服务器已经无法再接受新链接了
            close(sock);
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            fdarray_[i] = sock;//添加
        }
    }
    void HandlerEvent(fd_set &rfds)
    {
        //有就绪事件之后，我们还需要判断是什么就绪事件，是新链接还是老链接的数据传送
        for(int i = 0; i < N; ++i)
        {
            if (fdarray_[i] == defaultfd)
                continue;
            
            if(fdarray_[i] == listensock_.Fd() && (FD_ISSET(fdarray_[i], &rfds)))
            {
                //新的就绪事件是新链接
                Accepter();
            }
            else if(fdarray_[i] != listensock_.Fd() && (FD_ISSET(fdarray_[i], &rfds)))
            {
                //老链接的数据传送
                //这时候再去进行读取，就不会阻塞，因为select保证了读取缓冲区中一定有数据到来了
                char buffer[1024];
                ssize_t n = recv(fdarray_[i], buffer, sizeof(buffer)-1, 0);
                if(n > 0)
                {
                    buffer[n-1] = 0;
                    std::cout << buffer << std::endl;
                }
                else
                {
                    if (n == 0)
                        logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fdarray_[i], defaultfd);
                    else
                        logMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fdarray_[i], defaultfd);
                    close(fdarray_[i]);
                    fdarray_[i] = defaultfd;//取消select的管理
                }
            }
        }
    }
    void Start()
    {
        // 1. 这里我们能够直接获取新的链接吗？
        // 2. 最开始的时候，我们的服务器是没有太多的sock的，甚至只有一个sock！listensock
        // 3. 在网络中， 新连接到来被当做 读事件就绪！
        fdarray_[0] = listensock_.Fd();
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = fdarray_[0];
            for(int i = 0; i < N; ++i)
            {
                if(fdarray_[i] == defaultfd) continue;
                //合法的fd，添加到rfds中,让select关心
                FD_SET(fdarray_[i], &rfds);
                //更新最大的fd
                if(maxfd < fdarray_[i]) maxfd = fdarray_[i];
            }
            //struct timeval timeout = {2, 0};                                         // 2秒内阻塞等待
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr); // 最后一个参数为空的话表示阻塞等待
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout, %d: %s", errno, strerror(errno)); // 没有资源就绪
                break;
            case -1:
                logMessage(Warning, "%d: %s", errno, strerror(errno)); // 失败
                break;
            default:
                // 成功了
                logMessage(Debug, "有一个就绪事件发生了: %d", n);
                HandlerEvent(rfds);
                DebugPrint();
                break;
            }
        }
    }

    void DebugPrint()
    {
        std::cout << "fdarray[]: ";
        for (int i = 0; i < N; i++)
        {
            if (fdarray_[i] == defaultfd)
                continue;
            std::cout << fdarray_[i] << " ";
        }
        std::cout << "\n";
    }
    ~SelectServer()
    {
        listensock_.Close();
    }

private:
    uint16_t _port;
    Sock listensock_;
    type_t fdarray_[N]; // 管理所有的fd
};